#include "graphicsview.h"
#include "point.h"
#include "line.h"
#include "rectangle.h"
#include "circle.h"
#include <QGraphicsSceneMouseEvent>

GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent)
{
    scene_ = new QGraphicsScene(this);
    setScene(scene_);
    setRenderHint(QPainter::Antialiasing);
    setMouseTracking(true);
    scene_->setSceneRect(0, 0, 800, 600);
}

void GraphicsView::setCurrentShapeType(ShapeType type)
{
    currentShapeType_ = type;
    selectedShape_ = nullptr;
    isDragging_ = false;
}

void GraphicsView::setCurrentColor(const QColor &color)
{
    currentColor_ = color;
    if (selectedShape_) {
        selectedShape_->setColor(color);
        scene_->update();
    }
}

void GraphicsView::deleteSelectedShape()
{
    if (selectedShape_) {
        scene_->removeItem(selectedShape_);
        delete selectedShape_;
        selectedShape_ = nullptr;
    }
}

void GraphicsView::clearAllShapes()
{
    scene_->clear();
    currentShape_ = nullptr;
    selectedShape_ = nullptr;
}

void GraphicsView::mousePressEvent(QMouseEvent *event)
{
    QPointF scenePos = mapToScene(event->pos());

    if (event->button() == Qt::LeftButton) {
        if (currentShapeType_ == ShapeType::None) {
            selectedShape_ = findShapeAtPosition(scenePos);
            if (selectedShape_) {
                startPos_ = scenePos;
                isDragging_ = true;
                selectedShape_->setSelected(true);
                scene_->update();
            }
        } else {
            createShape(scenePos);
        }
    }

    QGraphicsView::mousePressEvent(event);
}

void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    QPointF scenePos = mapToScene(event->pos());

    if (currentShapeType_ == ShapeType::None) {
        highlightNearbyPoints(scenePos);

        if (isDragging_ && selectedShape_) {
            QPointF offset = scenePos - startPos_;
            selectedShape_->moveBy(offset.x(), offset.y());
            startPos_ = scenePos;
            scene_->update();
        }
    } else if (currentShape_) {
        updateShape(scenePos);
    }

    QGraphicsView::mouseMoveEvent(event);
}

void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        if (currentShape_) {
            finishShape();
        } else if (isDragging_) {
            isDragging_ = false;
            if (selectedShape_) {
                selectedShape_->setSelected(false);
                scene_->update();
            }
        }
    }

    QGraphicsView::mouseReleaseEvent(event);
}

void GraphicsView::highlightNearbyPoints(const QPointF &pos)
{
    bool highlighted = false;

    for (QGraphicsItem *item : scene_->items()) {
        if (MyShape *shape = dynamic_cast<MyShape*>(item)) {
            shape->highlightControlPoints(pos);
            if (shape->isPointNear(pos)) {
                highlighted = true;
            }
        }
    }

    setCursor(highlighted ? Qt::PointingHandCursor : Qt::ArrowCursor);
    scene_->update();
}

MyShape *GraphicsView::findShapeAtPosition(const QPointF &pos)
{
    QList<QGraphicsItem*> items = scene_->items(pos);
    for (QGraphicsItem *item : std::as_const(items)) {
        if (MyShape *shape = dynamic_cast<MyShape*>(item)) {
            return shape;
        }
    }
    return nullptr;
}

void GraphicsView::createShape(const QPointF &pos)
{
    switch (currentShapeType_) {
    case ShapeType::Point:
        currentShape_ = new Point(pos, currentColor_);
        break;
    case ShapeType::Line:
        currentShape_ = new Line(pos, pos, currentColor_);
        break;
    case ShapeType::Rectangle:
        currentShape_ = new Rectangle(pos, pos, currentColor_);
        break;
    case ShapeType::Circle:
        currentShape_ = new Circle(pos, 0, currentColor_);
        break;
    default:
        return;
    }

    scene_->addItem(currentShape_);
}

void GraphicsView::updateShape(const QPointF &pos)
{
    if (!currentShape_) return;

    switch (currentShapeType_) {
    case ShapeType::Line:
        if (Line *line = dynamic_cast<Line*>(currentShape_)) {
            line->setEndPoint(pos);
        }
        break;
    case ShapeType::Rectangle:
        if (Rectangle *rect = dynamic_cast<Rectangle*>(currentShape_)) {
            rect->setEndPoint(pos);
        }
        break;
    case ShapeType::Circle:
        if (Circle *circle = dynamic_cast<Circle*>(currentShape_)) {
            qreal radius = QLineF(circle->getStartPoint(), pos).length();
            circle->setRadius(radius);
        }
        break;
    default:
        break;
    }

    scene_->update();
}

void GraphicsView::finishShape()
{
    if (currentShape_) {
        currentShape_ = nullptr;
    }
}
QPixmap GraphicsView::getScenePixmap() const
{
    QPixmap pixmap(scene()->sceneRect().size().toSize());
    pixmap.fill(Qt::white);

    QPainter painter(&pixmap);
    scene()->render(&painter);
    return pixmap;
}

void GraphicsView::loadFromPixmap(const QPixmap& pixmap)
{
    scene()->clear();
    QGraphicsPixmapItem* item = scene()->addPixmap(pixmap);
    item->setPos(0, 0);
    setSceneRect(pixmap.rect());
}
